/*
* Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

#include <cerrno>
#include <filesystem>
#include <functional>
#include <optional>
#include <sstream>
#include <string>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <vector>
#include "iremote_stub.h"
#include "system_ability.h"
#include "system_ability_definition.h"
#include "interfaces/c++/MWServer.h"
#include "ISAService.h"
#include "log.h"

#define DEFAULT_RELEASE_ROOT "/data/local/release"
#define DEFAULT_DORA_RELEASE_ROOT "/data/local/release_dora"
#define DEFAULT_ROS_RELEASE_ROOT "/data/local/release_ros"
#define DEFAULT_ROS2_RELEASE_ROOT "/data/local/release_ros2"

namespace OHOS {
namespace OHMW {
DEFINE_OHMW_LOG_LABEL("OhmwSaService");

std::optional<std::string> SearchServerBinary(const std::string &name, const std::vector<const char *> &paths) {
    for (auto path : paths) {
        std::string binary = std::string(path) + "/" + name;
        if (access(binary.c_str(), X_OK) == 0) {
            return binary;
        }
    }
    return std::nullopt;
}

std::string CommandToString(const std::vector<const char *> &args) {
    std::stringstream ss;
    bool first = true;
    for (auto arg : args) {
        if (arg == nullptr) {
            continue;
        }

        if (first) {
            first = false;
        } else {
            ss << ' ';
        }

        ss << arg;
    }
    return ss.str();
}

void RunCommand(const std::vector<const char *> &args) {
    pid_t pid = fork();
    if (pid == 0) {
        std::vector<const char *> envs = {}; // TODO

        OHMW_LOGD("Run command: %{public}s", CommandToString(args).c_str());
        int ret = execve(args[0], const_cast<char *const *>(args.data()), const_cast<char *const *>(envs.data()));
        if (ret == -1) {
            OHMW_LOGE("Failed to execute command: %{public}s", strerror(errno));
        }
        exit(ret);
    } else if (pid > 0) {
        return;
    } else {
        OHMW_LOGE("Failed to fork process: %{public}s", strerror(errno));
    }
}

class OhmwSaService : public SystemAbility, public IRemoteStub<IOhmwSaService> {
public:
    DECLARE_SYSTEM_ABILITY(OhmwSaService);
    DISALLOW_COPY_AND_MOVE(OhmwSaService);

    explicit OhmwSaService(int32_t systemAbilityId, bool runOnCreate = true) {}

    ~OhmwSaService() override {}

    void OnStart() override {
        OHMW_LOGD("Start OHMW SA service.");
        bool res = Publish(this);
        if (!res) {
            OHMW_LOGE("Failed to publish OHMW SA service!");
            return;
        }

        if (!std::filesystem::exists("/dev/shm")) {
            if (mkdir("/dev/shm", 0755) == -1) {
                OHMW_LOGE("Failed to create /dev/shm: %{public}s", strerror(errno));
            }
        }
    }

    void OnStop() override {
        OHMW_LOGD("Stop OHMW SA service.");
    }

    ErrCode LaunchOhmwServer(MWServer::Type type) override {
        switch (type) {
            case MWServer::Type::DORA: {
                auto binary = SearchServerBinary(
                    "ohmw_dora_server",
                    {
                        DEFAULT_DORA_RELEASE_ROOT "/usr/bin",
                        DEFAULT_RELEASE_ROOT "/usr/bin",
                        "/system/bin",
                    }
                );
                if (binary.has_value()) {
                    RunCommand({binary->c_str()});
                    return OHMW_SA_ERR_SUCCESS;
                } else {
                    OHMW_LOGE("Failed to find ohmw_dora_server binary!");
                    return OHMW_SA_ERR_FAILED;
                }
                break;
            }
            case MWServer::Type::ROS: {
                auto binary = SearchServerBinary(
                    "ohmw_ros_server",
                    {
                        DEFAULT_ROS_RELEASE_ROOT "/usr/bin",
                        DEFAULT_RELEASE_ROOT "/usr/bin",
                        "/system/bin",
                    }
                );
                if (binary.has_value()) {
                    RunCommand({binary->c_str()});
                    return OHMW_SA_ERR_SUCCESS;
                } else {
                    OHMW_LOGE("Failed to find ohmw_ros_server binary!");
                    return OHMW_SA_ERR_FAILED;
                }
                break;
            }
            case MWServer::Type::ROS2: {
                auto binary = SearchServerBinary(
                    "ohmw_ros2_server",
                    {
                        DEFAULT_ROS2_RELEASE_ROOT "/usr/bin",
                        DEFAULT_RELEASE_ROOT "/usr/bin",
                        "/system/bin",
                    }
                );
                if (binary.has_value()) {
                    RunCommand({binary->c_str()});
                    return OHMW_SA_ERR_SUCCESS;
                } else {
                    OHMW_LOGE("Failed to find ohmw_ros2_server binary!");
                    return OHMW_SA_ERR_FAILED;
                }
                break;
            }
            default: {
                break;
            }
        }
        return OHMW_SA_ERR_FAILED;
    }

    int OnRemoteRequest(uint32_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option) override {
        if (data.ReadInterfaceToken() != GetDescriptor()) {
            OHMW_LOGE("OHMW SA service stub token verification error: %{public}d", code);
            return OHMW_SA_ERR_FAILED;
        }

        switch (code) {
            case OHMW_SA_CMD_LAUNCH_OHMW_SERVER: {
                int exception = data.ReadInt32();
                if (exception) {
                    return OHMW_SA_ERR_FAILED;
                }
                MWServer::Type type = static_cast<MWServer::Type>(data.ReadUint8());
                ErrCode ret = LaunchOhmwServer(type);
                reply.WriteInt32(0);
                reply.WriteInt32(ret);
                return 0;
            }
            default: {
                OHMW_LOGE("unsupported command, code %{public}u", code);
                reply.WriteInt32(0);
                reply.WriteInt32(OHMW_SA_ERR_NOT_SUPPORTED);
                return IPCObjectStub::OnRemoteRequest(code, data, reply, option);
            }
        }
        return 0;
    }
};

REGISTER_SYSTEM_ABILITY_BY_ID(OhmwSaService, OHMW_SA_SERVICE_ID, true)
}
}
